package com.juyu.miliao.common.dataSource.tool.operate;

import com.juyu.miliao.common.dataSource.key.DataSourceKey;
import com.juyu.miliao.common.dataSource.key.DataSourceKeyEvaluator;
import com.juyu.miliao.common.dataSource.tool.StrategyParams;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.util.stream.IntStream;

@Aspect
public class OperateAspect {
    private final Logger logger = LoggerFactory.getLogger(OperateAspect.class);
    private final DataSourceKey dataSourceKey;
    private final DataSourceKeyEvaluator dataSourceKeyEvaluator;
    private final LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();

    public OperateAspect(DataSourceKey dataSourceKey, DataSourceKeyEvaluator dataSourceKeyEvaluator) {
        this.dataSourceKey = dataSourceKey;
        this.dataSourceKeyEvaluator = dataSourceKeyEvaluator;
    }

    @Pointcut("@annotation(com.juyu.miliao.common.dataSource.tool.operate.Operate)")
    public void inOperateAnnotation() {
    }

    @Before("inOperateAnnotation()")
    public void setDataSourceKey(JoinPoint jp) throws NoSuchMethodException, SecurityException {
        if (logger.isDebugEnabled())
            logger.debug("设置当前 dataSourceKey");
        Method method = ((MethodSignature) jp.getSignature()).getMethod();
        Operate annotation = method.getAnnotation(Operate.class);
        //如果明确指定了数据源名称则直接使用
        if (StringUtils.isNotBlank(annotation.subDs())) {
            dataSourceKey.set(annotation.subDs());
            return;
        }
        //根据策略确定使用的数据源
        Object[] args = jp.getArgs();
        String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
        StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
        IntStream.range(0, parameterNames.length).forEach(i -> {
            evaluationContext.setVariable(parameterNames[i], args[i]);
        });
        ExpressionParser parser = new SpelExpressionParser();
        StrategyParams strategyParams = new StrategyParams();
        parser.parseExpression(annotation.value()).getValue(evaluationContext, strategyParams);
        String key = dataSourceKeyEvaluator.strategy(strategyParams.getStrategy(), strategyParams.getParam());
        dataSourceKey.set(key);
        if (logger.isDebugEnabled())
            logger.debug("设置当前 dataSourceKey 为：" + key);
    }

    public DataSourceKeyEvaluator getDataSourceKeyEvaluator() {
        return dataSourceKeyEvaluator;
    }

    @After("inOperateAnnotation()")
    public void removeDataSourceKey() {
        dataSourceKey.remove();
        if (logger.isDebugEnabled())
            logger.debug("清空当前 dataSourceKey");
    }

}
